#ifndef OSCTHULHUCLIENT_H
#define OSCTHULHUCLIENT_H

#include "QString"
#include "externals/includes/ip/UdpSocket.h"
#include "externals/includes/osc/OscReceivedElements.h"
#include "externals/includes/osc/OscOutboundPacketStream.h"
class ClientOSCThread;
#include "ClientOSCThread.h"
//#include "QMap"
#include "QHash"
#include "ClientUser.h"
#include <QReadWriteLock>
#include <QObject>
#include "QSemaphore"
#include "SyncObject.h"
#include "gui/MainWindow.h"
#include <QList>
#include "QDebug"
#include "QPointer"
class LoginThread;
#include "LoginThread.h"
#include "clientalivetimer.h"
#include <QSettings>

class OSCthulhuClient : public QObject
{
    Q_OBJECT
public:
    OSCthulhuClient(QString serverAddress, QString serverPassword, QString userName, QString userPassword, int serverPort,int clientPort,int appPort,QSemaphore* wait4CleanupDone,QSettings* settings);
    void sendServer(osc::OutboundPacketStream p);
    void login();
    void logout();
    QString getUserName();
    QString getUserPassword();
    int getClientPort();
    int getServerPort();
    QString getServerPassword();
    UdpTransmitSocket* getTransmitSocket();
    bool containsPeer(QString userName);
    void addPeer(QString userName);
    void removePeer(QString userName);
    ClientUser* getPeer(QString userName);
    void printUsers();
    void requestRemoveSyncObject(QString name);
    void requestAddSyncObject(QString name);
    void requestSetSyncArg(QString name, int argNum,int argValue);
    void requestSetSyncArg(QString name, int argNum,float argValue);
    void requestSetSyncArg(QString name, int argNum,double argValue);
    void requestSetSyncArg(QString name, int argNum,QString argValue);
    bool addSyncObject(QString name,QString type,QString subtype);
    void removeSyncObject(QString name);
    void addSyncArg(QString name,QVariant* argValue);
    void setSyncArg(QString name,int argNum,QVariant* argValue);

    void localSetSyncArg(QString name, int argNum, QVariant* argValue, int blockedPort);

    void finalizeSyncObject(QString name);
    void newStateSync();
    bool addTempSyncObject(QString name,QString type,QString subtype);
    void addTempSyncArg(QString name,QVariant* argValue);
    void finalizeStateSync();
    void sendChat( QString line );
    void receiveChat( QString line );
    void getChat();
    void connectedToServer();
    void appLogin();
    void sendToApp(osc::OutboundPacketStream p);

    void localSendToApp(osc::OutboundPacketStream p, int blockedPort);

    void changeAppPorts(QList<int> appPorts);
    void printMessage(osc::ReceivedMessage message);
    MainWindow mw;

    void resetAliveTimer();

    void changeUserPiece(QString user, QString piece);

private:

    void buildObjectString(QString name, QPointer<SyncObject> syncObject);

    QSemaphore* wait4CleanupDone;
    QString serverAddress;
    QString serverPassword;
    QString userName;
    QString userPassword;
    int serverPort;
    int clientPort;
    int appPort;
    QList<int> appPorts;
    UdpTransmitSocket* appSocket;
    ClientOSCThread* oscThread;
    QHash<QString,QPointer<ClientUser> > peers;
    QHash<QString,QPointer<SyncObject> > syncObjects;
    QHash<QString,QPointer<SyncObject> > tempSyncObjects;
    QReadWriteLock lock,syncObjectLock,tempSyncObjectLock;
    LoginThread* loginThread;
    bool connected, objectPrinting, oscPrinting;
    int aliveTimer;
    ClientAliveTimer* cat;
    QSettings* settings;

public slots:
    void handleQuit();
    void sendRemoveSyncObject(QString name,QString type,QString subType);
    void sendSyncObject(QString name);

    void sendSyncArg(QString name,int argNum);
    void localSendSyncArg(QString name, int argNum, int blockedPort);

    void sendAddPeer(QString userName);
    void sendRemovePeer(QString userName);
    void sendServerSlot(osc::OutboundPacketStream p);
    void flush();
    void setObjectPrinting(bool printing);
    void setOSCPrinting(bool printing);
    void increaseAliveTimer();
    void setPreferences(QString userName,QString userPassword,QString serverAddress,QString serverPassword,int serverPort,int clientPort,int appPort);

signals:
    void peerAdded(QString userName);
    void peerRemoved(QString userName);
    void syncArgChanged(QString name,int argNum);

    void localSyncArgChanged(QString name, int argNum, int blockedPort);

    void syncObjectAdded(QString name);
    void syncObjectRemoved(QString name,QString type,QString subType);
    void printObject(QString name, QStringList arguments);
    void removePrintObject(QString name);
    void printOSC(QString message);
    void connectedGUI();
    void disconnectedGUI();
    void changeGUIPiece(QString user,QString piece);
};

#endif // OSCTHULHUCLIENT_H
